const ROWS = 4;
const NUMBERS = [2, 4];
const MIN_LENGTH = 0.1; // 最小拖动距离
const MOVE_DURATION = 0.1; // 移动时长

cc.Class({
    extends: cc.Component,

    properties: {

        // 分数
        scoreLabel: cc.Label,
        score:0,
        // 预置块
        blockPrefab: cc.Prefab,
        // 间隔
        gap: 20,
        bg: cc.Node
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start () {
        this.drawBgBlocks();
        this.init();
        this.addEventHandler();
    },

    // 绘制格子
    drawBgBlocks () {
        this.blockSize = (cc.winSize.width - this.gap * (ROWS+1)) / ROWS;
        let x = this.gap + this.blockSize / 2;
        let y = this.blockSize;
        this.positions = [];
        for (let i = 0; i < ROWS; ++i) {
            this.positions.push([0, 0, 0, 0]);
            for (let j = 0; j < ROWS; ++j) {
                // 画出一个block
                let pos = cc.v2(x, y);
                this.drawOneBlock(x, y, pos);
                x += this.gap + this.blockSize;
                this.positions[i][j] = pos;
            }
            y += this.gap + this.blockSize;
            x = this.gap + this.blockSize / 2;
        }
    },
    drawOneBlock(x, y, pos, number=0) {
        // 画出一个block
        let block = cc.instantiate(this.blockPrefab);
        block.width = this.blockSize;
        block.height = this.blockSize;
        this.bg.addChild(block);
        block.setPosition(pos);
        block.getComponent('block').setNumber(number);
        return block;
    },

    // initialize
    init() {
        this.updateScore(0);
        // clear blocks
        if (this.blocks) {
            for (let i=0; i<this.blocks.length; ++i) {
                for (let j=0; j<this.blocks[i].length; ++j) {
                    if (this.blocks[i][j] !== null) {
                        this.blocks[i][j].destroy();
                    }
                }
            }
        }
        // all blocks
        this.blocks = [];
        // store number
        this.data = [];
        for (let i=0; i<ROWS; ++i) {
            this.blocks.push([null, null, null, null]);
            this.data.push([0, 0, 0, 0]);
        }
        this.addBlock();
        this.addBlock();
        this.addBlock();
    },
    // update score
    updateScore(number) {
        this.score = number;
        this.scoreLabel.string = '分数： ' + number;
    },

    /**
     * find empty block
     * @returns {[]}
     */
    getEmptyLocations() {
        let locations = [];
        for (let i=0; i<this.blocks.length; ++i) {
            for (let j=0; j<this.blocks[i].length; ++j) {
                if (this.blocks[i][j] === null) {
                    locations.push({x: i, y: j});
                }
            }
        }
        return locations;
    },

    /**
     * add one block
     * @returns {boolean}
     */
    addBlock() {
        let locations = this.getEmptyLocations();
        if (locations.length === 0) return false;
        let location = locations[Math.floor(Math.random() * locations.length)];
        let x = location.x;
        let y = location.y;
        let position = this.positions[x][y];
        let n = NUMBERS[Math.floor(Math.random()*NUMBERS.length)];
        this.blocks[x][y] = this.drawOneBlock(x, y, position, n);
        this.data[x][y] = n;
        return true;
    },

    // 监听事件
    // listen ontouchstart
    addEventHandler() {
        this.bg.on('touchstart', (e) => {
            this.startPoint = e.getLocation();
        } );

        this.bg.on('touchend', (e) => {
            this.touchEnd(e);
        });

        this.bg.on('touchcancel', (e) => {
           this.touchEnd(e);
        });
    },

    touchEnd(e) {
        this.endPoint = e.getLocation();
        let vec = this.endPoint.sub(this.startPoint);
        if (vec.mag() > MIN_LENGTH) {
            if (Math.abs(vec.x) > Math.abs(vec.y)) {
                // 水平方向
                if (vec.x > 0) {
                    this.moveRight();
                } else {
                    this.moveLeft();
                }
            } else {
                // 垂直方向
                if (vec.y > 0){
                    this.moveUp();
                } else {
                    this.moveDown();
                }
            }
        }
    },

    checkFail() {
        for (let i=0; i < ROWS; ++i) {
            for(let j=0; j < ROWS; ++j) {
                let n = this.data[i][j];
                if (n === 0) return false;
                // 判断上下左右是否有相同数字
                if(j > 0 && this.data[i][j-1] === n) return false;
                if(j < ROWS-1 && this.data[i][j+1] === n) return false;
                if(i > 0 && this.data[i-1][j] === n) return false;
                if(i < ROWS-1 && this.data[i+1][j] === n) return false;
            }
        }
        return true;
    },
    gameOver() {
      cc.log('game over!');
    },

    afterMove(hasMoved) {
        if(hasMoved) {
            this.updateScore(this.score+1);
            this.addBlock();
        }
        if (this.checkFail()) {
            this.gameOver();
        }
    },

    /**
     * 移动格子
     * @param {cc.Node} block 
     * @param {cc.Vec2} position
     * @param {function} callback
     */
    doMove(block, position, callback) {
        let action = cc.moveTo(MOVE_DURATION, position);
        let finish = cc.callFunc(() => {
            callback && callback();
        });
        // do action
        block.runAction(cc.sequence(action, finish));
    },

    moveLeft() {
        cc.log('move left');
        let hasMoved = false;
        let move = (x, y, callback) => {
            if(y === 0 || this.data[x][y] === 0) {
                // 到顶点，没有可以移动的格子
                callback && callback();
            } else if (this.data[x][y-1] === 0) {
                // move
                // 左边的格子为空
                let block = this.blocks[x][y];
                let pos = this.positions[x][y-1];
                this.blocks[x][y-1] = block;
                this.data[x][y-1] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, pos, ()=>{
                    // 下轮继续移动
                    move(x, y-1, callback);
                });
                hasMoved = true;
            } else if (this.data[x][y-1] === this.data[x][y]) {
                // merge
                // 相邻格子的数字相同
                let block = this.blocks[x][y];
                let pos = this.positions[x][y-1];
                this.data[x][y-1] *= 2;
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.blocks[x][y-1].getComponent('block').setNumber(this.data[x][y-1]);
                this.doMove(block, pos, ()=>{
                    block.destroy();
                    // 左边已经空了
                    callback && callback();
                });
                hasMoved = true;
            } else {
                // 相邻数字不相同
                callback && callback();
            }
        };
        let toMove = [];
        for(let i = 0; i < ROWS; ++i) {
            for(let j = 0; j < ROWS; ++j) {
                if (this.data[i][j] !== 0) {
                    toMove.push({x: i, y: j});
                }
            }
        }
        let counter = 0;
        for(let i = 0; i < toMove.length; ++i) {
            move(toMove[i].x, toMove[i].y, () =>{
                counter++;
                if (counter === toMove.length) {
                    this.afterMove(hasMoved);
                }
            });
        }
    },

    moveRight() {
        cc.log('move right');
        let hasMoved = false;
        let move = (x, y, callback) => {
            if(y === ROWS-1 || this.data[x][y] === 0) {
                // 到顶点，没有可以移动的格子
                callback && callback();
            } else if (this.data[x][y+1] === 0) {
                // move
                // 左边的格子为空
                let block = this.blocks[x][y];
                let pos = this.positions[x][y+1];
                this.blocks[x][y+1] = block;
                this.data[x][y+1] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, pos, ()=>{
                    // 下轮继续移动
                    move(x, y+1, callback);
                });
                hasMoved = true;
            } else if (this.data[x][y+1] === this.data[x][y]) {
                // merge
                // 相邻格子的数字相同
                let block = this.blocks[x][y];
                let pos = this.positions[x][y+1];
                this.data[x][y+1] *= 2;
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.blocks[x][y+1].getComponent('block').setNumber(this.data[x][y+1]);
                this.doMove(block, pos, ()=>{
                    block.destroy();
                    // 左边已经空了
                    callback && callback();
                });
                hasMoved = true;
            } else {
                // 相邻数字不相同
                callback && callback();
            }
        };
        let toMove = [];
        for(let i = 0; i < ROWS; ++i) {
            for(let j = ROWS-1; j >= 0; --j) {
                if (this.data[i][j] !== 0) {
                    toMove.push({x: i, y: j});
                }
            }
        }
        let counter = 0;
        for(let i=0; i<toMove.length; ++i) {
            move(toMove[i].x, toMove[i].y, () =>{
                counter++;
                if (counter === toMove.length) {
                    this.afterMove(hasMoved);
                }
            });
        }
    },


    moveUp() {
        cc.log('move up');
        let hasMoved = false;
        let move = (x, y, callback) => {
            if(x === ROWS-1 || this.data[x][y] === 0) {
                // 到顶点，没有可以移动的格子
                callback && callback();
            } else if (this.data[x+1][y] === 0) {
                // move
                // 左边的格子为空
                let block = this.blocks[x][y];
                let pos = this.positions[x+1][y];
                this.blocks[x+1][y] = block;
                this.data[x+1][y] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, pos, ()=>{
                    // 下轮继续移动
                    move(x+1, y, callback);
                });
                hasMoved = true;
            } else if (this.data[x+1][y] === this.data[x][y]) {
                // merge
                // 相邻格子的数字相同
                let block = this.blocks[x][y];
                let pos = this.positions[x+1][y];
                this.data[x+1][y] *= 2;
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.blocks[x+1][y].getComponent('block').setNumber(this.data[x+1][y]);
                this.doMove(block, pos, ()=>{
                    block.destroy();
                    // 左边已经空了
                    callback && callback();
                });
                hasMoved = true;
            } else {
                // 相邻数字不相同
                callback && callback();
            }
        };
        let toMove = [];
        for(let i = ROWS-1; i >= 0; --i) {
            for(let j = 0; j < ROWS; ++j) {
                if (this.data[i][j] !== 0) {
                    toMove.push({x: i, y: j});
                }
            }
        }
        let counter = 0;
        for(let i=0; i<toMove.length; ++i) {
            move(toMove[i].x, toMove[i].y, () =>{
                counter++;
                if (counter === toMove.length) {
                    this.afterMove(hasMoved);
                }
            });
        }
    },

    moveDown() {
        cc.log('move down');
        let hasMoved = false;
        let move = (x, y, callback) => {
            if(x === 0 || this.data[x][y] === 0) {
                // 到顶点，没有可以移动的格子
                callback && callback();
            } else if (this.data[x-1][y] === 0) {
                // move
                // 下边的格子为空
                let block = this.blocks[x][y];
                let pos = this.positions[x-1][y];
                this.blocks[x-1][y] = block;
                this.data[x-1][y] = this.data[x][y];
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.doMove(block, pos, ()=>{
                    // 下轮继续移动
                    move(x-1, y, callback);
                });
                hasMoved = true;
            } else if (this.data[x-1][y] === this.data[x][y]) {
                // merge
                // 相邻格子的数字相同
                let block = this.blocks[x][y];
                let pos = this.positions[x-1][y];
                this.data[x-1][y] *= 2;
                this.data[x][y] = 0;
                this.blocks[x][y] = null;
                this.blocks[x-1][y].getComponent('block').setNumber(this.data[x-1][y]);
                this.doMove(block, pos, ()=>{
                    block.destroy();
                    // 左边已经空了
                    callback && callback();
                });
                hasMoved = true;
            } else {
                // 相邻数字不相同
                callback && callback();
            }
        };
        let toMove = [];
        for(let i = 0; i < ROWS; ++i) {
            for(let j = 0; j < ROWS; ++j) {
                if (this.data[i][j] !== 0) {
                    toMove.push({x: i, y: j});
                }
            }
        }
        let counter = 0;
        for(let i=0; i<toMove.length; ++i) {
            move(toMove[i].x, toMove[i].y, () =>{
                counter++;
                if (counter === toMove.length) {
                    this.afterMove(hasMoved);
                }
            });
        }
    },
});
